#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <memory>
#include <ostream>
#include <vector>

#include "config.pb.h"

uint32_t bytes_to_uint32(const std::vector<uint8_t> &bytes)
{
    if (bytes.size() < sizeof(uint32_t))
    {
        // 处理字节数不足的情况
        throw std::runtime_error("Insufficient bytes for conversion");
    }
    uint32_t value = 0;
    value          |= (bytes[0] & 0xFF) << 24;
    value          |= (bytes[1] & 0xFF) << 16;
    value          |= (bytes[2] & 0xFF) << 8;
    value          |= (bytes[3] & 0xFF);
    return value;
}

void config_init(PB_TerninalConfigration &config)
{
    std::unique_ptr<PB_DeviceInfo> device_info = std::make_unique<PB_DeviceInfo>();
    device_info->set_terminal_sn("1234567809");
    device_info->set_log_level(2);

    /* 结构体字段有copyfrom和mergefrom两个操作，都可以从其它对象拷贝到成员字段 */
    config.mutable_device_info()->CopyFrom(*device_info);
    // config->mutable_device_info()->MergeFrom(*deviceInfo);

    std::unique_ptr<PB_NetInfo> net_info = std::make_unique<PB_NetInfo>();
    net_info->set_ip(bytes_to_uint32({192, 168, 1, 2}));
    net_info->set_dns(bytes_to_uint32({8, 8, 8, 8}));
    net_info->set_gateway(bytes_to_uint32({192, 168, 1, 1}));
    net_info->set_use_dhcp(true);
    /* !!!!!! protobuf中的bytes类型被转换成了std::string类型 */
    /* 这里不是c语言的zero-terminated字符串，可以传入\0字符 */
    std::vector<uint8_t> mac_addr = {'A', 0x00, 'B', 'C', 'D', 'E'};
    net_info->set_mac_addr(std::string(mac_addr.begin(), mac_addr.end()));
    /* 如果不包含\0字符，可以简化写成这样 */
    // net_info->set_mac_addr(
    //         "\x01"
    //         "ABCDEF");

    config.mutable_net_info()->CopyFrom(*net_info);

    std::unique_ptr<PB_ServerInfo> server_info = std::make_unique<PB_ServerInfo>();
    /* string类型还是string类型 */
    server_info->set_hostname("dengyongsheng.cn");
    server_info->set_port(8888);
    config.mutable_server_info()->CopyFrom(*server_info);
}

static void dump_hex(const void *data, uint32_t size);

int main(int argc, char *argv[])
{
    std::unique_ptr<PB_TerninalConfigration> config = std::make_unique<PB_TerninalConfigration>();
    config_init(*config);

    /* 原始数据 */
    std::cout << "********************" << std::endl << "原始数据：" << std::endl << config->DebugString() << std::endl;

    /* 数据序列化 */
    std::string data;
    config->SerializeToString(&data);
    std::cout << "********************" << std::endl << "序列化后的数据：" << std::endl;
    dump_hex(data.c_str(), data.size());
    std::cout << std::endl;

    /* 数据反序列化 */
    std::unique_ptr<PB_TerninalConfigration> config2 = std::make_unique<PB_TerninalConfigration>();
    config2->ParseFromString(data);
    std::cout << "********************" << std::endl << "反序列化后的数据：" << std::endl << config2->DebugString() << std::endl;

    std::cout << "********************" << std::endl << "序列化前后比较：" << std::endl;
    if (config->DebugString() == config2->DebugString())
    {
        std::cout << "对象一致" << std::endl;
    }
    else
    {
        std::cout << "对象不一致" << std::endl;
    }
    return 0;
}

void dump_hex(const void *data, uint32_t size)
{
    char   ascii[17];
    size_t i, j;
    ascii[16] = '\0';
    for (i = 0; i < size; ++i)
    {
        printf("%02X ", ((unsigned char *)data)[i]);
        if (((unsigned char *)data)[i] >= ' ' && ((unsigned char *)data)[i] <= '~')
        {
            ascii[i % 16] = ((unsigned char *)data)[i];
        }
        else
        {
            ascii[i % 16] = '.';
        }
        if ((i + 1) % 8 == 0 || i + 1 == size)
        {
            printf(" ");
            if ((i + 1) % 16 == 0)
            {
                printf("|  %s \n", ascii);
            }
            else if (i + 1 == size)
            {
                ascii[(i + 1) % 16] = '\0';
                if ((i + 1) % 16 <= 8)
                {
                    printf(" ");
                }
                for (j = (i + 1) % 16; j < 16; ++j)
                {
                    printf("   ");
                }
                printf("|  %s \n", ascii);
            }
        }
    }
}
